#include "PointerType.h"

#include "GenericInfo.h"
#include "PlaceholderType.h"
#include "TypeInstance.h"


CString CPointerType::PlaceholderName = U"T";

CPointerType::CPointerType()
    :CType(
        STypeID(U"TNativePointer",true)
        ,ETypeFlags::Generic | ETypeFlags::RawPointer
        )
{
    GenericInfo=new CGenericInfo();

    CPlaceholderType* ValuePlaceholderType = 
        new CPlaceholderType(
            STypeID(GetFullName()+U"::"+PlaceholderName,true)
            ,ETypeFlags::None
            ,0
            ,this);
    GenericInfo->PlaceholderTypes.Add(ValuePlaceholderType);
}

CPointerType *CPointerType::GetStaticType()
{
    static CPointerType* StaticType=new CPointerType();
    return StaticType;
}

CPointerGeneralized *CPointerType::FindPointerType(const SDecoratedType & ValueType)
{
    auto Found = reinterpret_cast<CGenericInfo*>(CPointerType::GetStaticType()
        ->GenericInfo)
        ->GenericMap.Find({ValueType.BasicType});

    if (Found)
    {
        return (CPointerGeneralized *)*Found;
    }

    return nullptr;
}

CString CPointerType::CreateTypeName(const CString &ElementType)
{
    return STypeID::CreateTemplateTypeName(
        CPointerType::GetStaticType()->GetFullName()
        ,{ElementType}
        ,{PlaceholderName}
        );
}

STypeID CPointerType::CreateID(const SDecoratedType &ElementType)
{
    return STypeID(
        CPointerType::GetStaticType()->ID
        ,{ElementType.BasicType->ID}
        ,{PlaceholderName}
        );
}

class CRawPointerInstance : public ITypeInstance
{
protected:
    CPointerGeneralized* Type;
public:
    CRawPointerInstance(CPointerGeneralized* InType)
        :Type(InType)
    {
    }

    void InitInstance(void* Memory) override
    {
        reinterpret_cast<void**>(Memory)[0]=nullptr;
    }

    void CloneInstance(const void* Instance, void* Memory) override
    {
        reinterpret_cast<void**>(Memory)[0]=reinterpret_cast<void* const*>(Instance)[0];
    }

    void EraseInstance(void* InValue) override
    {
        reinterpret_cast<void**>(InValue)[0]=nullptr;
    }

    void* CreateInstance() override
    {
        void* Memory=malloc(sizeof(void*));
        reinterpret_cast<void**>(Memory)[0]=nullptr;
        return Memory;
    }

    void ResetAllocated(void* InMemory) override
    {
        reinterpret_cast<void**>(InMemory)[0]=nullptr;
    }

    uint32_t GetSize() override
    {
        return sizeof(void*);
    }

    uint64_t GetInstanceHash(const void* InInstance) const override
    {
        return reinterpret_cast<uint64_t>(reinterpret_cast<const void* const*>(InInstance)[0]);
    }

    bool IsInstanceEqual(const void* A, const void* B) const override
    {
        return reinterpret_cast<const void* const *>(A)[0]==reinterpret_cast<const void* const*>(B)[0];
    }

    CString ToDebugString(const void* InInstance) const override
    {
        return U"&"+Type->GetValueType().BasicType->ToDebugString(reinterpret_cast<void* const*>(InInstance)[0]);
    }


};


CPointerGeneralized::CPointerGeneralized(const SDecoratedType & InValueType)
    :CType(
        CPointerType::CreateID(InValueType)
        ,  ETypeFlags::RawPointer| ETypeFlags::Generalized
            | (InValueType.BasicType->IsScriptType() ? ETypeFlags::Script : ETypeFlags::None)
            |(InValueType.BasicType->CanUseInScript() ? ETypeFlags::None : ETypeFlags::CantUseInScript)
        )
{
    assert(!InValueType.BasicType->IsRawPointerType()&& "not designe for pointer's pointer");

    Instancable=new CRawPointerInstance(this);

    GeneralizedInfo=new CGeneralizedInfo();
    GeneralizedInfo->TemplateType=CPointerType::GetStaticType();
    GeneralizedInfo->TypeArguments.Add(InValueType.BasicType);

    GeneralizedInfo->TemplateType->GenericInfo->GeneralizedTypes.Add(this);
    reinterpret_cast<CGenericInfo*>(GeneralizedInfo->TemplateType->GenericInfo)->GenericMap.Add(TVector<CType*>({InValueType.BasicType}),this);

}

SDecoratedType CPointerGeneralized::GetValueType() const
{
    return GeneralizedInfo->TypeArguments[0];
}

void *CPointerGeneralized::GetPointedObject(const void *InInstance)
{
    return reinterpret_cast<void* const*>(InInstance)[0];
}

void* CPointerGeneralized::CreateInstance(const void* InValuePtr)
{
    void* Memory = malloc(sizeof(void*));
    reinterpret_cast<const void**>(Memory)[0] = InValuePtr;
    return Memory;
}
